﻿#include "icmpv4.h"
#include "ipv4.h"
#include "protocol.h"
#include "raw.h"

#if DBG_DISP_ENABLED(DBG_ICMPV4_LEVEL)
static void display_icmpv4_pkt(const char *title, icmpv4_pkt_t *pkt)
{
    plat_printf("=================%s==================\n", title);
    plat_printf("type:%d  code:%d  checksum:%04x\n", pkt->hdr.type, pkt->hdr.code, pkt->hdr.checksum);
    plat_printf("=============================================\n");
}
#else
#define display_icmpv4_pkt(title, pkt)
#endif

/**
 * @brief 检验收到的icmpv4数据包
 * @param pkt 收到的icmpv4数据包
 * @param size icmpv4数据包大小
 * @param buf 数据包
 */
static bool icmpv4_is_pkt_ok(icmpv4_pkt_t *pkt, int size, pktbuf_t *buf)
{
    if (size < sizeof(icmpv4_hdr_t))
    {
        DBG_WARNING(DBG_ICMPV4_LEVEL, "the icmpv4 pkt size:%d shorter than shortest size:%d!", size, sizeof(icmpv4_hdr_t));
        return false;
    }

    uint16_t checksum = pktbuf_checksum16(buf, size, 0, true);
    if (checksum)
    {
        DBG_WARNING(DBG_ICMPV4_LEVEL, "the icmpv4 checksum is not zero!");
        return false;
    }

    return true;
}

/**
 * @brief 发送icmpv4数据包
 * @param dest 被发送数据包的目的ip地址
 * @param src 被发送数据包的源ip地址
 * @param buf 被发送的数据包
 */
net_err_t icmpv4_out(ipaddr_t *dest, ipaddr_t *src, pktbuf_t *buf)
{
    icmpv4_pkt_t *pkt = (icmpv4_pkt_t *)pktbuf_data(buf);

    pktbuf_reset_acc(buf);
    pkt->hdr.checksum = pktbuf_checksum16(buf, buf->total_size, 0, true);

    display_icmpv4_pkt("icmpv4 pkt out", pkt);

    net_err_t err = ipv4_out(NET_PROTOCOL_ICMPv4, dest, src, buf);
    if (err < NET_ERR_OK)
    {
        DBG_ERROR(DBG_ICMPV4_LEVEL, "send icmpv4 pkt by ipv4 failed! err type:%d!", err);
        pktbuf_free(buf);
    }

    return NET_ERR_OK;
}

/**
 * @brief 收到request类型数据包,对其进行处理
 * @param dest 收到的数据包的  目的ip地址
 * @param src 收到的数据包的   源ip地址
 * @param buf 收到的数据包
 */
static net_err_t icmpv4_echo_reply(ipaddr_t *dest, ipaddr_t *src, pktbuf_t *buf)
{
    icmpv4_pkt_t *pkt = (icmpv4_pkt_t *)pktbuf_data(buf);

    pkt->hdr.type = ICMPV4_ECHO_REPLY;
    pkt->hdr.checksum = 0;

    return icmpv4_out(src, dest, buf);
}

/**
 * @brief icmpv4的初始化工作
 */
net_err_t icmpv4_init()
{
    DBG_INFO(DBG_ICMPV4_LEVEL, "icmpv4 init begin!");

    DBG_INFO(DBG_ICMPV4_LEVEL, "icmpv4 init end!");
    return NET_ERR_OK;
}

/**
 * @brief 对收到的icmpv4协议的包进行处理
 * @param src_ip 收到的包的发送方ip地址
 * @param netif_ip 接收到该包的网络接口ip地址
 * @param buf 接收到的数据包
 */
net_err_t icmpv4_in(ipaddr_t *src_ip, ipaddr_t *netif_ip, pktbuf_t *buf)
{
    DBG_INFO(DBG_ICMPV4_LEVEL, "icmpv4 in!");

    // 这里并没有直接移除ipv4协议的包头,因为后面还会用到
    net_err_t err = pktbuf_set_cont(buf, sizeof(ipv4_hdr_t));
    if (err < NET_ERR_OK)
    {
        DBG_ERROR(DBG_ICMPV4_LEVEL, "pktbuf set cont:%d failed in icmpv4 in! err_type = %d!", sizeof(ipv4_hdr_t), err);
        return err;
    }
    ipv4_pkt_t *ipv4_pkt = (ipv4_pkt_t *)pktbuf_data(buf);
    int ipv4_hdrsize = ipv4_hdr_size(ipv4_pkt);
    err = pktbuf_set_cont(buf, ipv4_hdrsize + sizeof(icmpv4_hdr_t));
    if (err < NET_ERR_OK)
    {
        DBG_ERROR(DBG_ICMPV4_LEVEL, "pktbuf set cont:%d failed in icmpv4 in! err_type = %d!", sizeof(ipv4_hdr_t), err);
        return err;
    }

    // err = pktbuf_remove_header(buf, ipv4_hdrsize);
    // if (err < 0)
    // {
    //     DBG_ERROR(DBG_ICMPV4_LEVEL, "remove headr:%d failed in icmpv4 in! err_type = %d", ipv4_hdrsize);
    //     return err;
    // }
    // 计算校验和前先初始化数据包的读取位置到开头
    icmpv4_pkt_t *pkt = (icmpv4_pkt_t *)(pktbuf_data(buf) + ipv4_hdrsize);
    display_icmpv4_pkt("icmpv4 pkt in", pkt);

    // 检验收到的包
    pktbuf_seek(buf, ipv4_hdrsize);
    if (!icmpv4_is_pkt_ok(pkt, buf->total_size - ipv4_hdrsize, buf))
    {
        DBG_WARNING(DBG_ICMPV4_LEVEL, "the icmpv4 pkt is error!");
        return NET_ERR_BROKEN;
    }

    switch (pkt->hdr.type)
    {
    case ICMPV4_ECHO_REQUEST:
    {
        err = pktbuf_remove_header(buf, ipv4_hdrsize);
        if (err < 0)
        {
            DBG_ERROR(DBG_ICMPV4_LEVEL, "remove headr:%d failed in icmpv4 in! err_type = %d", ipv4_hdrsize);
            return err;
        }
        return icmpv4_echo_reply(netif_ip, src_ip, buf);
        break;
    }

    default:
        return raw_in(buf);
        break;
    }

    pktbuf_free(buf);
    return NET_ERR_OK;
}

/**
 * @brief 通过icmpv4回复端口不可达
 * @param dest_ip 发送目的ip地址
 * @param src_ip 源ip地址
 * @param uint8_t icmpv4包头code字段数值
 * @param buf 要进行发送的ipv4数据包
 */
net_err_t icmpv4_out_unreach(ipaddr_t *dest_ip, ipaddr_t *src_ip, uint8_t code, pktbuf_t *buf)
{
    int copy_size = ipv4_hdr_size((ipv4_pkt_t *)pktbuf_data(buf)) + UNREACH_HDR_ORIGINAL_DATAGRAM;
    copy_size = copy_size > buf->total_size ? buf->total_size : copy_size;
    pktbuf_t *newbuf = pktbuf_alloc(copy_size + sizeof(icmpv4_hdr_t) + UNREACH_HDR_UNUSED);
    if (!newbuf)
    {
        DBG_ERROR(DBG_IPV4_LEVEL, "alloc pkt(size:%d) failed!", copy_size + sizeof(icmpv4_hdr_t) + UNREACH_HDR_UNUSED);
        return NET_ERR_NULLP;
    }

    if (pktbuf_set_cont(buf, sizeof(icmpv4_hdr_t) + sizeof(uint32_t)) < NET_ERR_OK)
    {
        DBG_ERROR(DBG_ICMPV4_LEVEL, "pktbuf set cont:%d failed!", sizeof(icmpv4_hdr_t) + sizeof(uint32_t));
    }
    icmpv4_pkt_t *pkt = (icmpv4_pkt_t *)pktbuf_data(newbuf);
    pkt->hdr.type = ICMPV4_UNREACH;
    pkt->hdr.code = code;
    pkt->hdr.checksum = 0;
    pkt->reverse = 0;
    pktbuf_reset_acc(newbuf);
    pktbuf_reset_acc(buf);
    pktbuf_seek(newbuf, sizeof(icmpv4_hdr_t) + UNREACH_HDR_UNUSED);
    net_err_t err = pktbuf_copy(newbuf, buf, copy_size);
    if (err < NET_ERR_OK)
    {
        DBG_ERROR(DBG_ICMPV4_LEVEL, "pktbuf copy failed!");
        pktbuf_free(newbuf);
        return err;
    }

    err = icmpv4_out(dest_ip, src_ip, newbuf);
    if (err < NET_ERR_OK)
    {
        pktbuf_free(newbuf);
    }

    return NET_ERR_OK;
}